Aller au contenu principal

Optional

Optional<T> est un conteneur introduit en Java 8 qui représente une valeur qui peut être présente ou absente. Il remplace l'usage de null pour signaler l'absence de résultat, et force le code appelant à gérer explicitement ce cas.

Pourquoi Optional?

Sans Optional, retourner null crée des NullPointerException silencieux :

// DANGEREUX — peut lancer NullPointerException
public Etudiant trouverParNom(String nom) {
// ... retourne null si pas trouvé
}

Etudiant e = trouverParNom("Alice");
System.out.println(e.getNote()); // NullPointerException si null

Avec Optional, l'absence de valeur est explicite dans le type de retour :

public Optional<Etudiant> trouverParNom(String nom) {
// ... retourne Optional.empty() si pas trouvé
}

Créer un Optional

// Valeur présente — on est certain que la valeur n'est pas null
Optional<String> avecValeur = Optional.of("Alice");

// Valeur absente — on sait qu'il n'y a rien
Optional<String> vide = Optional.empty();

// Valeur qui pourrait être null — on n'est pas certain
Optional<String> peutEtreNull = Optional.ofNullable(null); // produit un Optional vide
Optional<String> peutEtreNull2 = Optional.ofNullable("Bob"); // produit Optional avec "Bob"

of vs ofNullable — quelle différence ?

Imaginons une méthode qui cherche un étudiant et peut retourner null si non trouvé :

String nom = trouverNom(); // peut retourner null

// of(null) — plante immédiatement avec NullPointerException
Optional<String> opt1 = Optional.of(nom); // DANGER si nom est null

// ofNullable(null) — produit un Optional vide, sans exception
Optional<String> opt2 = Optional.ofNullable(nom); // sûr, gère le null

La règle est simple :

  • Optional.of(v) → utiliser seulement si on est certain que v n'est pas null
  • Optional.ofNullable(v) → utiliser quand v pourrait être null

Vérifier et extraire la valeur

Optional<String> opt = Optional.of("Alice");

// Vérifier si une valeur est présente
if (opt.isPresent()) {
System.out.println(opt.get()); // Alice
}

// Raccourci : ifPresent() avec une lambda
opt.ifPresent(v -> System.out.println(v));
opt.ifPresent(System.out::println); // identique avec référence de méthode

get() sur un Optional vide lance NoSuchElementException. Toujours vérifier avec isPresent() avant.

Valeur par défaut

orElse — valeur fixe de remplacement

Si l'Optional est vide, retourne la valeur passée en argument. Si l'Optional contient quelque chose, retourne cette valeur.

Optional<String> avecValeur = Optional.of("Alice");
Optional<String> vide = Optional.empty();

System.out.println(avecValeur.orElse("inconnu")); // Alice ← valeur présente, on l'utilise
System.out.println(vide.orElse("inconnu")); // inconnu ← vide, on utilise le défaut

orElseGet — valeur calculée à la demande

Même idée qu'orElse, mais au lieu de passer une valeur fixe, on passe un Supplier — une lambda sans argument qui calcule la valeur de remplacement. Cette lambda n'est exécutée que si l'Optional est vide.

Optional<String> vide = Optional.empty();

// La lambda () -> ... n'est appelée que si vide est... vide
String resultat = vide.orElseGet(() -> "valeur calculée");
System.out.println(resultat); // valeur calculée

C'est utile quand produire la valeur par défaut est coûteux (requête BD, calcul long) — on évite de le faire si ce n'est pas nécessaire.

orElseThrow — erreur explicite si absent

Si l'Optional est vide, lance l'exception fournie. Force le code appelant à traiter le cas d'absence comme une situation anormale.

Optional<String> vide = Optional.empty();

// Lance IllegalStateException si vide
String resultat = vide.orElseThrow(() -> new IllegalStateException("Étudiant non trouvé"));
Optional<String> avecValeur = Optional.of("Alice");

// Aucune exception : retourne "Alice"
String nom = avecValeur.orElseThrow(() -> new IllegalStateException("Étudiant non trouvé"));
System.out.println(nom); // Alice

Utilisation avec les Streams

findFirst() et findAny() retournent un Optional car il est possible qu'aucun élément ne corresponde :

import java.util.List;
import java.util.Optional;

public class OptionalStream {
public static void main(String[] args) {
List<String> noms = List.of("Alice", "Bob", "Clara");

// Trouver le premier nom qui commence par "C"
Optional<String> trouve = noms.stream()
.filter(n -> n.startsWith("C"))
.findFirst();

// Option 1 : valeur par défaut
System.out.println(trouve.orElse("aucun")); // Clara

// Option 2 : traitement conditionnel
trouve.ifPresent(n -> System.out.println("Trouvé : " + n));

// Cas où rien n'est trouvé
Optional<String> absent = noms.stream()
.filter(n -> n.startsWith("Z"))
.findFirst();

System.out.println(absent.orElse("aucun")); // aucun
}
}

Résumé des méthodes clés

MéthodeDescription
Optional.of(v)Crée un Optional avec une valeur non-null
Optional.empty()Crée un Optional vide
Optional.ofNullable(v)Crée un Optional, vide si v est null
isPresent()true si une valeur est présente
get()Retourne la valeur (exception si vide)
orElse(défaut)Retourne la valeur ou le défaut
orElseGet(supplier)Retourne la valeur ou calcule un défaut
orElseThrow(supplier)Retourne la valeur ou lance une exception
ifPresent(consumer)Exécute une action si valeur présente